package com.eva.eip.cms.biz.category;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.eva.eip.cms.biz.category.vo.CmsCategoryNodeVO;
import com.eva.eip.cms.databuilder.bo.TemplateDataParameters;
import com.eva.eip.dao.cms.model.CmsCategory;
import com.eva.eip.dao.cms.model.CmsTemplate;
import com.eva.eip.framework.common.constants.ResponseStatus;
import com.eva.eip.framework.common.exception.BusinessException;
import com.eva.eip.framework.common.utils.AssertUtil;
import com.eva.eip.service.cms.CmsCategoryService;
import com.eva.eip.service.cms.CmsTemplateService;
import com.eva.eip.service.common.FreemarkerService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class CmsCategoryBiz {

    // 栏目UID参数名称
    private static final String PARAMETER_CATEGORY_UID = "categoryUid";

    @Resource
    private CmsCategoryService categoryService;

    @Resource
    private CmsTemplateService cmsTemplateService;

    @Resource
    private FreemarkerService freemarkerService;

    /**
     * 查询栏目树
     * v1.3.0调整，统一查询栏目树方法
     *
     * @param withHidden 是否包含隐藏的栏目
     * @return List<CmsCategoryNode>
     */
    public List<CmsCategoryNodeVO> findTree(Boolean withHidden) {
        return this.findTree(withHidden, null);
    }

    /**
     * 查询栏目树
     * v1.3.0调整，统一查询栏目树方法
     *
     * @param withHidden 是否包含隐藏的栏目
     * @param parameters 模板数据参数，用于编译路径，如分页参数等，如果没有，则使用模板自带的参数值进行编译
     * @return List<CmsCategoryNode>
     */
    public List<CmsCategoryNodeVO> findTree(Boolean withHidden, TemplateDataParameters parameters) {
        AssertUtil.notNull(withHidden, "缺少withHidden参数");
        // 查询所有栏目
        QueryWrapper<CmsCategory> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .eq(!withHidden, CmsCategory::getHidden, Boolean.FALSE)
                .eq(CmsCategory::getDeleted, Boolean.FALSE)
                .orderByAsc(CmsCategory::getSort);
        List<CmsCategory> categories = categoryService.findList(queryWrapper);

        // 获取栏目树各节点绑定的模板ID集，用于获取模板集
        Set<Integer> templateIds = categories.stream()
                .map(CmsCategory::getTemplateId)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());

        // 查询模板并根据ID进行分组，方便栏目从模板中获取并补充uri访问路径
        Map<Integer, CmsTemplate> cmsTemplateMap = cmsTemplateService.findByIds(templateIds)
                .stream()
                .collect(Collectors.toMap(CmsTemplate::getId, cmsTemplate -> cmsTemplate));

        // 转为栏目树
        List<CmsCategoryNodeVO> nodes = categoryService.transferToTree(categories, CmsCategoryNodeVO.class);

        // 补充常规栏目的uri
        this.paddingCategoryUri(nodes, cmsTemplateMap);

        // 编译栏目的uri
        this.compileCategoryUri(nodes, parameters == null ? new TemplateDataParameters() : parameters);
        return nodes;
    }

    /**
     * 查询子栏目树
     * v1.3.0调整，统一查询栏目树方法
     *
     * @param parentCategoryUid 父栏目UID
     * @param withHidden 是否包含隐藏的栏目
     * @return List<CmsCategoryNodeVO>
     */
    public List<CmsCategoryNodeVO> findSubTree (String parentCategoryUid, Boolean withHidden) {
        return this.findSubTree(parentCategoryUid, withHidden, null);
    }

    /**
     * 查询子栏目树
     * v1.3.0调整，统一查询栏目树方法
     *
     * @param parentCategoryUid 父栏目UID
     * @param withHidden 是否包含隐藏的栏目
     * @param parameters 模板数据参数，用于编译路径，如分页参数等，如果没有，则使用模板自带的参数值进行编译
     * @return List<CmsCategoryNodeVO>
     */
    public List<CmsCategoryNodeVO> findSubTree (String parentCategoryUid, Boolean withHidden, TemplateDataParameters parameters) {
        List<CmsCategoryNodeVO> nodes = this.findTree(withHidden, parameters);
        List<CmsCategoryNodeVO> subTree = this.findSubTree(parentCategoryUid, nodes);
        if (subTree == null) {
            return Collections.emptyList();
        }
        return subTree;
    }

    /**
     * 查找栏目树
     *
     * @param parentCategoryUid 父栏目UID
     * @param range 查找数据范围
     * @return List<CmsCategoryNodeVO>
     */
    private List<CmsCategoryNodeVO> findSubTree (String parentCategoryUid, List<CmsCategoryNodeVO> range) {
        for (CmsCategoryNodeVO node : range) {
            // 如果匹配到了父节点，则返回父节点下的所有子节点
            if (node.getUid().equals(parentCategoryUid)) {
                if (node.getChildren() != null) {
                    return (List<CmsCategoryNodeVO>) node.getChildren();
                }
                return Collections.emptyList();
            }
            // 未匹配到，则进行递归查找
            if (node.getChildren() != null) {
                List<CmsCategoryNodeVO> children = this.findSubTree(parentCategoryUid, (List<CmsCategoryNodeVO>)node.getChildren());
                if (children != null) {
                    return children;
                }
            }
        }
        return null;
    }

    /**
     * v1.3.0新增
     * 补充常规栏目的访问地址，将访问地址
     *
     * @param nodes 栏目节点
     */
    protected void paddingCategoryUri (List<CmsCategoryNodeVO> nodes, Map<Integer, CmsTemplate> cmsTemplateMap) {
        for (CmsCategoryNodeVO node : nodes) {
            // 常规栏目访问地址为模板的访问地址
            if (node.getTemplateId() != null) {
                CmsTemplate template = cmsTemplateMap.get(node.getTemplateId());
                if (template == null) {
                    throw new BusinessException(ResponseStatus.SERVER_ERROR, "栏目配置有误！");
                }
                node.setUri(cmsTemplateMap.get(node.getTemplateId()).getAccessUri());
            }
            if (!CollectionUtils.isEmpty(node.getChildren())) {
                this.paddingCategoryUri((List<CmsCategoryNodeVO>)node.getChildren(), cmsTemplateMap);
            }
        }
    }

    /**
     * v1.3.0新增
     * 编译栏目访问路径，得到最终栏目的访问地址
     *
     * @param nodes 栏目节点
     * @param parameters 模板参数
     */
    protected void compileCategoryUri (List<CmsCategoryNodeVO> nodes, TemplateDataParameters parameters) {
        for (CmsCategoryNodeVO node : nodes) {
            if (StringUtils.isNotBlank(node.getUri())) {
                // 拷贝参数
                TemplateDataParameters copyParameters =
                        JSON.parseObject(JSON.toJSONString(parameters), TemplateDataParameters.class);
                // 补充当前栏目唯一标识
                copyParameters.put(PARAMETER_CATEGORY_UID, node.getUid());
                // 如果栏目存在配置，则将预设的参数配置写入参数，供编译访问路径使用
                if (StringUtils.isNotBlank(node.getTemplateParameters())) {
                    JSONObject targetPresetParametersJson = null;
                    try {
                        targetPresetParametersJson = JSON.parseObject(node.getTemplateParameters());
                    } catch (JSONException ignore) {}
                    if (targetPresetParametersJson == null) {
                        log.warn("无法正确解析栏目 " + node.getTitle() + " 的模板参数");
                    }
                    if (targetPresetParametersJson != null) {
                        for (String key : targetPresetParametersJson.keySet()) {
                            List<TemplateDataParameters.PresetParameter> presetParameters =
                                    JSONArray.parseArray(targetPresetParametersJson.getString(key), TemplateDataParameters.PresetParameter.class);
                            for (TemplateDataParameters.PresetParameter presetParameter : presetParameters) {
                                copyParameters.assignIfNotEmpty(presetParameter.getName(), presetParameter.getValue());
                            }
                        }
                    }
                }
                try {
                    String compiledUri = freemarkerService.compile(node.getUri(), node.getUri(), copyParameters);
                    node.setUri(compiledUri);
                } catch (BusinessException e) {
                    log.error("编译栏目路径失败！栏目: {}, 栏目路径表达式: {}", node.getUid(), node.getUri());
                    if (e.getCode().equals(ResponseStatus.TEMPLATE_ERROR.getCode())) {
                        throw new RuntimeException("栏目配置有误！");
                    }
                }
            }
            if (!CollectionUtils.isEmpty(node.getChildren())) {
                this.compileCategoryUri((List<CmsCategoryNodeVO>)node.getChildren(), parameters);
            }
        }
    }
}
